package cn.tedu.tmall.admin.mall.service.impl;

import cn.tedu.tmall.admin.mall.dao.repository.IMallCategoryRepository;
import cn.tedu.tmall.admin.mall.pojo.param.MallCategoryAddParam;
import cn.tedu.tmall.admin.mall.pojo.po.MallCategoryPO;
import cn.tedu.tmall.admin.mall.pojo.vo.MallCategoryTreeVO;
import cn.tedu.tmall.admin.mall.service.IMallCategoryService;
import cn.tedu.tmall.common.ex.ServiceException;
import cn.tedu.tmall.common.web.ServiceCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class MallCategoryService implements IMallCategoryService {

    @Autowired
    private IMallCategoryRepository mallCategoryRepository;

    /**
     * 简单本地缓存  https://blog.csdn.net/zzzili/article/details/131326504
     */
    private Map<String,List<MallCategoryTreeVO>> cached = new HashMap<>();

    /**
     * 增加类别
     * 1 名称不能重复 重复报错
     * 2 类别的级别是 子级还是顶级
     *     顶级的 parent_id == 0  子级 parent_id不为0
     * 3 如果是子级 需要判断父级是否存在 太原要判断山西是否存在
     * 4 如果是父级 直接保存  山西直接保存
     * 5 判断父级是否禁用  可选
     * 6 计算深度
     *    父级 parentId=0  深度 = 1
     *    子级   深度 = 父级的深度 + 1
     * 7 计算isParent
     *     如果是父级 isParent = 0  山西 = 0
     *     如果是子级 把父级的isParent = 1  太原的父级山西  isParent = 1
     * @param param
     */
    @Override
    public void addCategory(MallCategoryAddParam param) {
        //1 名称不能重复 重复报错
        String name = param.getName();
        Integer count = mallCategoryRepository.countCategoryByName(name);
        if (count != 0 ){
            //存在
            //throw new ServiceException(ServiceCode.OK,"名称不能重复");魔数
            //            //throw new ServiceException(ServiceCode.OK,"名称不能重复");
            //            //throw new ServiceException(ServiceCodeEnum.OK.getCode(),ServiceCodeEnum.BAD_REQUEST.getMsg());
            //            //throw new ServiceException(ServiceCodeEnum.BAD_REQUEST,"名称不能重复");
            throw new ServiceException(ServiceCodeEnum.BAD_REQUEST_NAME_EXIST);
        }
        Long parentId = param.getParentId();
        Integer depth = 1;
        if (parentId == 0) {
            //顶级 直接保存
            MallCategoryPO mallCategoryPO = new MallCategoryPO();
            BeanUtils.copyProperties(param,mallCategoryPO);
            mallCategoryPO.setDepth(depth);
            mallCategoryPO.setIsParent(0);
            mallCategoryRepository.save(mallCategoryPO);
        }else {
            //子级
            //查询父级信息
            MallCategoryPO parentCategoryPO = mallCategoryRepository.getCategoryById(parentId);
            if (parentCategoryPO != null) {
                //判断是否启用 先不实现
                //计算深度
                //子级深度 = 父级的深度 + 1
                depth = parentCategoryPO.getDepth() + 1;
                //先保存子级 还是先更新他父级的isParent = 1
                MallCategoryPO mallCategoryPO = new MallCategoryPO();
                BeanUtils.copyProperties(param,mallCategoryPO);
                mallCategoryPO.setDepth(depth);
                mallCategoryPO.setIsParent(0);
                //mallCategoryPO.setGmtCreate();
                //mallCategoryPO.setGmtModified(); 每一个表都有这两个字段,都需要设置
                Integer rows = mallCategoryRepository.save(mallCategoryPO);
                if (rows != 0) {
                    //更新父级的isParent = 1
                    if (parentCategoryPO.getIsParent() == 0) {
                        MallCategoryPO update = new MallCategoryPO();
                        update.setId(parentId);
                        update.setIsParent(1);
                        mallCategoryRepository.updateCategoryById(update);
                    }
                }
            }else {
                throw new RuntimeException("父级不存在");
            }
        }
    }

    /**
     * 把平铺的数据表结构变成多层级的树形结构
     * @return
     */
    @Override
    public List<MallCategoryTreeVO> treeCategory() {
        //从数据库查询数据
        //A数据量较小 几百条  可以获取所有
        //B数据量较大 几万条 能不能获取所有呢?
        // 1 数据库能不能查出来 性能很差
        // 2 内存能不能接的住  如果接不住 OutMemoryError 内存溢出
        // todo 数据太大怎么解决
        //A 全查
        String key = "ALL";
        List<MallCategoryTreeVO> cachedVo = cached.get("ALL");
        if (!CollectionUtils.isEmpty(cachedVo)){
            return cachedVo;
        }else {
            List<MallCategoryPO> all =
                    mallCategoryRepository.getAll();
            log.debug("查询所有类别数据:{}",all);
            //把po转换为vo
            // 怎么把平铺的数据转换为层级的数据 todo
            //1 先取最顶级 parent = 0  北京  山西
            //2 取下一级  parent_id = 北京的id 山西的id  北京的下一级 海淀 山西的下一级是 太原
            //3 取下一级的下一级  parent_id = 海淀id parent_id = 太原id  海淀的下一级 没有终止 太原的下一级
            //4 .... 何时是个头呢,什么时候停止呢  1 判断当前级别还有没有子级 如果没了,stop
            //5 递归  自己调自己  终止条件 退出这个循环  -Xss jvm 栈 栈溢出
            //6 如果是明确的知道层级 层级很少 省市区 可以 简单的方法 一级一级的往下找

            //先取最顶级
            List<MallCategoryTreeVO> topCategory = getTopCategory(all);
            //获取顶级的子级
            //parent_id 等于顶级的id 就是顶级的子级
            for (MallCategoryTreeVO topVo : topCategory) {
                appendChild(topVo,all);
            }
            cached.put(key,topCategory);
            return topCategory;
        }

        //return mockData();
    }

    @Override
    public List<MallCategoryTreeVO> treeTopCategory() {
        //取最顶级
        List<MallCategoryPO> top = mallCategoryRepository.getTopCategory();
        log.debug("查询所有顶级类别数据:{}",top);
        return categoryPOS2VO2(top);
    }

    @Override
    public List<MallCategoryTreeVO> treeChildrenCategory(Long parentId) {
        List<MallCategoryPO> children = mallCategoryRepository.getChildrenCategory(parentId);
        return categoryPOS2VO2(children);
    }

    /**
     * 把PO列表转化为VO列表
     * @param pos
     * @return
     */
    private List<MallCategoryTreeVO> categoryPOS2VO2(List<MallCategoryPO>pos){
        List<MallCategoryTreeVO> result = new ArrayList<>();
        pos.forEach(po->{
            //转换为vo
            result.add(categoryPO2VO(po));
        });
        return result;
    }


    /**
     * 追加子级
     * 递归 1  自己调自己  2 终止条件 退出这个循环
     * 入参1 是给谁设置子级
     * 出参 是入参1的子级
     */
    private MallCategoryTreeVO appendChild(MallCategoryTreeVO vo,List<MallCategoryPO> all){
        //这个第一个入参Vo设置子级
        List<MallCategoryTreeVO> children = new ArrayList<>();
        for (MallCategoryPO mallCategoryPO : all) {
            //这两个家伙直接比较 用== 还是equals
            if (vo.getId().equals(mallCategoryPO.getParentId())) {
                MallCategoryTreeVO childVO = categoryPO2VO(mallCategoryPO);
                children.add(appendChild(childVO,all)); //去找childVo的子级
            }else {
                //没有子级了,终止条件 不调用appendChild,退出了
            }
        }
        vo.setChildren(children);
        return vo;
        //退出条件是没有子级 isParent = 0
        //if(//没找到子级 ) { //终止条件
        //退出
        //}else {
        //增加子级
        //子级获取到 增加的 children 列表里
        //MallCategoryTreeVO child = new MallCategoryTreeVO();
        //appendChild(child); //继续找子级

        //children.add(child); 导致没法继续找子级

        //}
        //return null;
    }

    //public static void main(String[] args) { //1行
    //   main(args); //2
    //调用逻辑  1 2 1 2 1 2
    //}

    /**
     * 从所有的类别中获取顶级类别
     * @param mallCategoryPOS
     * @return
     */
    private List<MallCategoryTreeVO> getTopCategory(List<MallCategoryPO> mallCategoryPOS) {
        List<MallCategoryTreeVO> result = new ArrayList<>();
        mallCategoryPOS.forEach(po->{
            if(po.getParentId() == 0){
                //取最顶级 并且 转换为vo
                result.add(categoryPO2VO(po));
            }
        });

        return result;
    }

    /**
     * PO转换为VO
     * @param po
     * @return
     */
    private MallCategoryTreeVO categoryPO2VO(MallCategoryPO po){
        MallCategoryTreeVO vo = new MallCategoryTreeVO();
        BeanUtils.copyProperties(po,vo);
        return vo;
    }

    private  List<MallCategoryTreeVO> mockData(){
        //mock  准备的假数据
        MallCategoryTreeVO bj = new MallCategoryTreeVO();
        bj.setId(1L);
        bj.setName("北京");

        MallCategoryTreeVO hd = new MallCategoryTreeVO();
        hd.setId(2L);
        hd.setName("海淀");

        List<MallCategoryTreeVO> bjchildren = new ArrayList<>();
        bjchildren.add(hd);
        bj.setChildren(bjchildren);


        MallCategoryTreeVO sx = new MallCategoryTreeVO();
        sx.setId(2L);
        sx.setName("山西");

        MallCategoryTreeVO ty = new MallCategoryTreeVO();
        ty.setId(3L);
        ty.setName("太原");

        List<MallCategoryTreeVO> sxchildren = new ArrayList<>();

        sxchildren.add(ty);
        sx.setChildren(sxchildren);


        MallCategoryTreeVO yz = new MallCategoryTreeVO();
        yz.setId(4L);
        yz.setName("迎泽");
        List<MallCategoryTreeVO> tychildren = new ArrayList<>();
        tychildren.add(yz);
        ty.setChildren(tychildren);


        List result = new ArrayList();
        result.add(bj);
        result.add(sx);

        return result;
    }
}
